---
sidebar_position: 2
---

# 2. Строковый класс

## Общие сведения

В языке С++ встроенный строковый тип отсутствует, но для более удобной работы со строками был разработан строковый класс.
Для работы со строковым классом необходимо подключить к программе заголовочный файл `<string>`. Строковый класс является классом-контейнером, поэтому поддерживает все алгоритмы библиотеки стандартных шаблонов.

:::caution
Примечание: `std::basic_string` не рассматривается стандартом как контейнер, но ведет себя очень похоже из-за своего сходства. Для удобства он указан в справочниках как "псевдоконтейнер".

Подробности смотрите [здесь](https://en.cppreference.com/w/cpp/container).
:::

Формат оператора описания строки:

```cpp
string имя;
```

Пример:

```cpp
string str1, str2;
```

Ввод-вывод строк. Для ввода-вывода строк используется как уже известные объекты `cin` и `cout`, так и библиотечные функции С++.

Ввод-вывод строки c помощью стандартных потоков ввода-вывода `cin` и `cout`:

```cpp
#include <iostream>
#include <string>

int main() {
  std::string str;
  std::cin >> str;
  std::cout << str << std::endl;
}
```

Строка вводится точно так же, как и переменные известных нам типов. Если запустите программу и введете строку из нескольких слов, выводится только первое слово. Это связано с тем, что ввод выполняется до первого пробельного символа (то есть пробела, знака табуляции или символа перевода строки `\n`).

Если требуется ввести строку, состоящую из нескольких слов, в одну строковую переменную, используется функция `getline`, первым параметром которой является поток `cin`, а второй параметр – имя переменной, которая получит строку, что введет пользователь с клавиатуры:

```cpp
#include <iostream>
#include <string>

int main() {
  std::string str;
  getline(std::cin, str);
  std::cout << str << std::endl;
}
```

Вывод строки происходит как вывод единого целого:

```cpp
std::cout << "строка = " << str1; 
```

## Операции со строками

Со строками можно выполнять следующие арифметические операции:

- `=` - присваивание значения;
- `+=` - добавление в конец строки другой строки или символа;
- `+` - конкатенация двух строк, конкатенация строки и символа;
- `==`, `!=` - посимвольное сравнение;
- `<`, `>`, `<=`, `>=` - лексикографическое сравнение.

То есть можно скопировать содержимое одной строки в другую при помощи операции `S1 = S2`, сравнить две строки на равенство при помощи `S1 == S2`, сравнить строки в лексикографическом порядке при помощи `S1 < S2`, или сделать сложение (конкатенацию) двух строк в виде `S = S1 + S2`.

Присвоение строк происходит с помощью операции присвоения:

```cpp
str1 = "Mary";
str2 = str1;
```

Доступ к отдельным символам строки может происходить также как к элементам массива:

```cpp
str1[2] = 'n'; // str1="Many";
```

Операция сцепления строк дописывает в строку еще одного слова или любую строку;

```cpp
str2 = str1 + "Hello";
```

## Получение длины строки

Методы `size()` и `length()` возвращают количество символов в строке. Рассмотрим пример:

```cpp
#include <iostream>
#include <string>

int main() {
  std::string str = "Миру - мир";
  std::cout << str.length() << std::endl;
}
```cpp

Если кодировка файла с кодом будет в UTF-8, то результат работы программы:

```bash
17
```

**UTF-8** (от англ. Unicode Transformation Format, 8-bit — "формат преобразования Юникода, 8-бит") — распространённый стандарт кодирования символов, позволяющий более компактно хранить и передавать символы Юникода, используя переменное количество байт (от 1 до 4), и обеспечивающий полную обратную совместимость с 7-битной кодировкой ASCII. Подробнее о реализации UTF-8 в С++ читайте [здесь](https://habr.com/ru/articles/731614/).

Вспомним о том, что тип `char` в С++ [занимает 1 байт памяти](https://learn.microsoft.com/ru-ru/cpp/cpp/data-type-ranges). Для хранения символов UTF-8 от не подходит, поэтому в С++ существует типы: `wchar_t`, `char16_t`, `char32_t`, `char8_t`, о которых подробнее можете узнать [здесь](https://en.cppreference.com/w/cpp/language/types).

Проанализируем символы, входящие в строку `Миру - мир`.

`М`, `и`, `р`, `у`, `м`, `и`, `р` - 7 символов. Каждый символ занимает по [2 байта](https://www.utf8-chartable.de/unicode-utf8-table.pl), так как они из UTF-8:

| Символ кодовой точки | Символ  | Шестнадцатеричное представление | Имя |
| --- | --- | --- |
| U+041C | `М` | `d0 9c` | CYRILLIC CAPITAL LETTER EM |
| U+0438 | `и` | `d0 b8` | CYRILLIC SMALL LETTER I |
| U+0440 | `р` | `d1 80` | CYRILLIC SMALL LETTER ER |
| U+0443 | `у` | `d1 83` | CYRILLIC SMALL LETTER U |
| U+043C | `м` | `d0 bc` | CYRILLIC SMALL LETTER EM |

`\s`, `-`, `\s`, - 3 символа. Эти символы из [кодовой таблицы ASCII](https://www.ascii-code.com/) и занимают 1 байт памяти.

В итоге получаем, что рассматриваемая строка занимает $7 * 2 + 3 = 14 + 3 = 17$ байтов памяти. Это ровно столько, сколько вернул метод `length()`.

Следующий код вернет более подходящий вариант, с учетом символов UTF-8:

```cpp
#include <iostream>
#include <string>

int main() {
  std::wstring str{L"Миру - мир"};
  std::cout << str.length() << std::endl;
}
```

В результате получим:

```bash
10
```

## Примеры работы со строками

### Пример 1

Даны два слова (две переменные). Сколько раз во втором слове встречается первая буква первого слова (не использовать `find`, `erase`, `substr` и т.д.).

```cpp
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string s1, s2;
  int result = 0;
  std::cout << "Введите первое слово: ";
  getline(std::cin, s1);
  std::cout << "Введите второе слово: ";
  getline(std::cin, s2);
  for (int i = 0; i < s2.length(); i++)
    if (s1[0] == s2[i]) result++;
  std::cout << "\nРезультат:" << std::endl;
  std::cout << result << std::endl;
}
```

Результат выполнения программы:

```bash
Введите первое слово: apple
Введите второе слово: apapapap

Результат:
4
```

### Пример 2

Даны 3 слова в 3-х разных переменных. Образовать новую последовательность символов, состоящую из первых букв каждого слова через пробел.

```cpp
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string s1, s2, s3, result;
  std::cout << "Введите первое слово: ";
  getline(std::cin, s1);
  std::cout << "Введите второе слово: ";
  getline(std::cin, s2);
  std::cout << "Введите третье слово: ";
  getline(std::cin, s3);
  result = s1[0];
  result += " ";
  result += s2[0];
  result += " ";
  result += s3[0];
  std::cout << "\nРезультат:" << std::endl;
  std::cout << result << std::endl;
}
```

Результат выполнения программы:

```bash
Введите первое слово: Take
Введите второе слово: My
Введите третье слово: Money

Результат:
T M M
```

### Пример 3

Имеется некоторая последовательность символов. Образовать новую последовательность, включив в нее символы исходной, кроме символов `g` и `v` (не использовать склейку `+`, `find`, `erase`, `substr` и т.д.).

```cpp
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string text, result = "";
  std::cout << "Введите текст: ";
  getline(std::cin, text);
  for (int i = 0; i < text.length(); i++)
    if (text[i] != 'g' && text[i] != 'v') result += text[i];
  std::cout << "\nРезультат:" << std::endl;
  std::cout << result << std::endl;
}
```

Результат выполнения программы:

```bash
Введите текст: Python is a programming language that lets you work quickly and integrate systems more effectivaly.

Результат:
Python is a prorammin lanuae that lets you work quickly and interate systems more effectialy.
```

Метод `c_str()` - возвращает указатель на область памяти, в которой хранятся символы строки, возвращает значение типа char*. Возвращаемое значение можно рассматривать как C-строку и использовать в функциях, которые должны получать на вход C-строку.

### Пример 4

Дан текст. Переставить в нем первую букву первого предложения и первую букву второго предложения (сначала найти номер первой точки, не использовать склейку `+`, `find`, `erase`, `substr` и т.д.).

```cpp
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string txt;
  std::cout << "Введите текст: ";
  getline(std::cin, txt);
  int i = 0, i_second = -1;
  while (1) {
    if (i >= txt.length()) break;
    if (txt[i] == '.') {
      i++;
      while (txt[i] == ' ') i++;
      i_second = i;
      break;
    }
    i++;
  }
  char ch = txt[0];
  if (i != 0 && i_second != -1) {
    txt[0] = txt[i_second];
    txt[i_second] = ch;
  }
  std::cout << "\nРезультат:" << std::endl;
  std::cout << txt << std::endl;
}
```

Результаты работы программы:

```bash
Введите текст: Winter is coming.        Soon.

Результат:
Sinter is coming.        Woon.
```

### Пример 5

Дан текст. Переписать в другую переменную только цифры, латинские буквы и пробелы (использовать склейку `+`, не использовать `find`, `erase`, `substr` и т.д.).

```cpp
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string txt, result = "";
  std::cout << "Введите текст: ";
  getline(std::cin, txt);
  for (int i = 0; i < txt.length(); i++) {
    if (txt[i] >= '0' && txt[i] <= '9')  // если символ - цифра
      result += txt[i];
    if (txt[i] >= 'a' && txt[i] <= 'z')  // если символ - строчная буква
      result += txt[i];
    if (txt[i] >= 'A' && txt[i] <= 'B')  // если символ - заглавная буква
      result += txt[i];
    if (txt[i] == ' ')  // если символ - пробел
      result += txt[i];
  }
  std::cout << "\nРезультат:" << std::endl;
  std::cout << result << std::endl;
}
```

Результаты работы программы:

```bash
Введите текст: все пройдет this too will pass 2021

Результат:
  this too will pass 2021
```

## Передача строк в качестве параметров функций

Переменные типа `string` передаются в функцию также, как переменные другого типа.

### Пример 6

Создать функцию `find` поиска в строке символа. Функция принимает два параметра: строку и символ, а возвращает или позицию первого включения символа в строку, или `-1`, если символ в строке не найден.

Используя функцию `find`, проверить наличие символа `@` в строке, заданной пользователем.

```cpp
#include <iostream>
#include <string>

int find(std::string s, char c) {
  for (int i = 0; i < s.length(); i++)
    if (s[i] == c) return i;
  return -1;
}

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  int n = 0;
  std::string line;
  char ch = '@';
  std::cout << "Введите текст: " << std::endl;
  getline(std::cin, line);
  if (find(line, ch) != -1)
    std::cout << "Есть";
  else
    std::cout << "Heт";
  std::cout << std::endl;
}
```

Результаты работы программы:

```bash
Введите текст: 
al@aaaa.ru
Есть
```

### Пример 7

Создать функцию `find` поиска в строке символа. Функция принимает два параметра: строку и символ, а возвращает или позицию первого включения символа в строку, или `-1`, если символ в строке не найден.

Используя функцию `find`, из текстового файла вывести на консоль только строки, в которых встречается символ `@`.

```cpp
#include <fstream>
#include <iostream>
#include <string>

int find(std::string s, char c) {
  for (int i = 0; i < s.length(); i++)
    if (s[i] == c) return i;
  return -1;
}

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  int n = 0;
  std::string line;
  char ch = '@';
  std::fstream F("text.txt");  // открываем файл в режиме чтения
  if (F) {  // если открытие файла прошло корректно, то
    // цикл для чтения значений из файла; выполнение цикла прервется,
    // когда достигнем конца файла, в этом случае F.eof() вернет истину.
    while (!F.eof()) {
      getline(F, line);  // чтение строки из потока F в line
      if (find(line, ch) != -1) {
        std::cout << line << std::endl;  // вывод line на экран
      }
    }
    F.close();  // закрытие потока
  } else {  // если открытие файла прошло некорректно, то вывод
    // сообщения об отсутствии такого файла
    std::cout << "Файл не существует" << std::endl;
  }
}
```

Результаты работы программы:

```bash
Aaaaddad@lll
```

### Пример 8

Дан текстовый файл. Запишите в другой файл только такие строки исходного файла, которые не содержат чисел.

Создадим функцию, которая принимает в качестве параметра строку, а возвращает или true, если в строке есть числа, или `false`, если числа в строке не найдены.

```cpp
#include <fstream>
#include <iostream>
#include <string>

bool is_digit(std::string line) {
  for (int i = 0; i < line.length(); i++)
    if (line[i] >= '0' && line[i] <= '9') return true;
  return false;
}

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string line;
  std::ifstream in("text.txt");  // открываем файл в режиме чтения
  std::ofstream out("result.txt");  // открываем файл в режиме записи
  if (in && out) {  // если открытие файлов прошло корректно, то
    // цикл для чтения значений из файла и записи; выполнение цикла прервется,
    // когда достигнем конца файла, в этом случае in.eof() вернет истину.
    while (1) {
      getline(in,
              line);  // чтение очередной строки из потока in в переменную line
      if (is_digit(line)) {
        out << line << std::endl;
        std::cout << line << std::endl;
      }
      if (in.eof()) break;  // выход из цикла, если конец файла достигнут
    }
    in.close();   // закрытие потока
    out.close();  // закрытие потока
  } else {  // если открытие файла прошло некорректно, то вывод
    // сообщения об отсутствии такого файла
    std::cout << "Файл не существует" << std::endl;
  }
}
```

Результаты работы программы:

```bash
weqwe231321
123adsad
12312sadasd
3dffdsf
```

### Пример 9

Дан текстовый файл. Запишите в другой файл содержимое исходного файла, в начале  каждой строки добавив `e-mail:`.
Создадим функцию, которая принимает в качестве параметра строку, а возвращает строку, у которой в начале добавлено  `e-mail:`..

```cpp
#include <fstream>
#include <iostream>
#include <string>

std::string email(std::string line) {
  line = "e-mail: " + line;
  return line;
}

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string line;
  std::ifstream in("text.txt");  // открываем файл в режиме чтения
  std::ofstream out("result.txt");
  if (in && out) {  // открываем файл в режиме записи
    // цикл для чтения значений из файла и
    // записи; выполнение цикла прервется, когда достигнем конца файла, в этом
    // случае in.eof() вернет истину.
    while (1) {
      getline(in,
              line);  // чтение очередной строки из потока in в переменную line
      out << email(line) << std::endl;
      std::cout << email(line) << std::endl;
      if (in.eof()) break;  // выход из цикла, если конец файла достигнут
    }
    in.close();   // закрытие потока
    out.close();  // закрытие потока
  } else {  // если открытие файла прошло некорректно, то вывод
    // сообщения об отсутствии такого файла
    std::cout << "Файл не существует" << std::endl;
  }
}
```

Результаты работы программы:

```bash
e-mail: Aaaaddad@lll
e-mail: weqwe231321
e-mail: 123adsad
e-mail: 12312sadasd
e-mail: 3dffdsf
e-mail: asdasdsadasd
```

## Пример поиска подстроки

Написать программу, которая определяет, встречается ли в заданном текстовом файле заданная последовательность символов. Длина строки текста не превышает `80` символов, текст не содержит переносов слов, последовательность не содержит пробельных символов.

### I. Исходные данные и результаты

Исходные данные:

1. Текстовый файл неизвестного размера, состоящий из строк длиной не более `80` символов. Поскольку по условию переносы отсутствуют, можно ограничиться поиском заданной последовательности в каждой строке отдельно. Следовательно, необходимо помнить только одну текущую строку файла. Для ее хранения выделим строковую переменную `line`.
2. Последовательность символов для поиска, вводимая с клавиатуры. Поскольку по условию задачи она не содержит пробельных символов, ее длина также не должна быть более 80 символов, иначе поиск завершится неудачей. Для ее хранения также выделим строковую переменную `word`.

Результатом работы программы является сообщение либо о наличии в каждой строке заданной последовательности, либо об ее отсутствии. Представим варианты сообщений в программе в виде строковых констант.

Для работы с файлом потребуется служебная переменная соответствующего типа.

### II. Алгоритм решения задачи

Для каждой строки проверять, содержится ли в ней заданная последовательность.

Печатать сообщение о наличии заданной последовательности или её отсутствии в каждой строке.

Если файл пустой, напечатать сообщение об отсутствии за¬данной последовательности и завершить программу.

### III. Программа и тестовые примеры

```cpp
#include <fstream>
#include <iostream>
#include <string>

int main() {
#ifdef WIN32
  system("chcp 65001");
#else
  setlocale(LC_ALL, "Russian");
#endif
  std::string word, line;  // 2
  std::cout << "Введите слово для поиска: ";
  std::cin >> word;
  std::ifstream fin("text.txt");  // 3
  if (!fin) {
    std::cout << "Ошибка открытия файла." << std::endl;
    return 1;  // 4
  }
  int Nomer = 0;                // 1
  while (getline(fin, line)) {  // 5
    std::cout << Nomer;
    if (line.find(word) == -1) {  // 6
      std::cout << " - отсутствует!" << std::endl;
    } else {
      std::cout << " - есть!" << std::endl;
    }
    Nomer++;
  }
  if (!Nomer) {
    std::cout << "Отсутствует!" << std::endl;
  }
}
```

Результаты работы программы:

```bash
Введите слово для поиска: we
0 - отсутствует!
1 - есть!
2 - отсутствует!
3 - отсутствует!
4 - отсутствует!
5 - отсутствует!
```

Рассмотрим помеченные операторы. В операторе 1 описывается переменная `Nomer` для хранения номера текущей строки. В операторе 2 описывается переменная `line` для размещения очередной строки файла и переменная `word` для размещения искомой последовательности символов.

В операторе 3 определяется объект `fin` класса входных потоков `ifstream`. С этим объектом можно работать так же, как со стандартными объектами `cin` и `cout`, то есть использовать операции помещения в поток `<<` и извлечения из потока >>.
Предполагается, что файл с именем `text.txt` находится в том же каталоге, что и текст программы, иначе следует указать полный путь, дублируя символ обратной косой черты, так как иначе он будет иметь специальное значение, например:

```cpp
ifstream fin("с:\\prim\\cpp\\text.txt");  // 3
```

В операторе 4 проверяется успешность создания объекта `fin`. Файлы, открываемые для чтения, проверять нужно обязательно! В операторе 5 организуется цикл чтения из файла в переменную `line`.

Функция `getline` успешном чтении строки возвращает значение true, при достижении конца файла вернет значение `false`, завершающее цикл.

Для анализа строки в операторе 6 применяется метод `line.find(word)`, который осуществляет поиск подстроки `word` в строке `line`. В случае успешного поиска функция возвращает указатель на найденную подстроку, в случае неудачи – значение `-1`.

В качестве тестового примера приготовьте текстовый файл, состоящий из нескольких строк. Для тестирования программы следует запустить ее по крайней мере два раза: введя с клавиатуры слово, содержащееся в файле, и слово, которого в нем нет. Файл должен быть в кодировке ANSI, иначе из-за разных кодировок слова, вводимого с клавиатуры, и текстового файла слово никогда не будет найдено в файле.

Даже такую простую программу мы рекомендуем вводить и отлаживать по шагам. Предлагаемая последовательность отладки:

1. Ввести "скелет" программы (директивы `#include`, функцию `main()`, операторы 1-4). Добавить контрольный вывод введенного слова. Запустив программу, проверить ввод слова и успешность открытия файла. Выполнить программу, задав имя несуществующего файла, для проверки вывода сообщения об ошибке. Удалить контрольный вывод слова.
2. Проверить цикл чтения из файла: добавить оператор 5 с его завершающей фигурной скобкой, внутри цикла поставить контрольный вывод прочитанной строки:

   ```cpp
   std::cout << line << std::endl;
   ```

   Удалить контрольный вывод строки.

3. Дополнить программу операторами проверки и вывода сообщений.
